package net.kingborn.core.command;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import net.kingborn.core.controller.BaseController;
import net.kingborn.core.entry.Result;
import net.kingborn.core.exception.BizException;
import net.kingborn.core.util.JsonKit;
import net.kingborn.core.util.StrKit;
import net.kingborn.core.util.Util;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;


public abstract class BaseCommand {

    private BaseController context;
    private Result result;
    protected JSONObject data;
    private JSONObject paramObj;

    public BaseCommand() {
        this.result = new Result();
        this.data = new JSONObject();
    }

    public Result doAction(JSONObject paramObj, BaseController context) throws Exception {
        this.paramObj = paramObj;
        this.context = context;

        initArgs();

        try {
            init();
            doCommand();

        } finally {
            doFinally();
        }

        result.setData(JSONObject.parseObject(JsonKit.toJson(data)));

        return result;
    }

    private void initArgs() throws IllegalAccessException {
        Field[] fields = this.getClass().getDeclaredFields();

        Param param = null;
        for (Field field : fields) {
            param = field.getAnnotation(Param.class);
            if (param != null) {
                // 对变量进行赋值
                handleField(field, param);
            }

        }
    }

    private void handleField(Field field, Param param) throws IllegalAccessException {
        field.setAccessible(true);

        String paramName = StrKit.notBlank(param.name()) ? param.name() : param.value();
        paramName = StrKit.notBlank(paramName) ? paramName : field.getName();

        Object paramValue = null;
        try {
            paramValue = paramObj.getObject(paramName, field.getType());

            // 考虑登录拦截器
            if (paramValue == null && "currentUser".equals(paramName)) {
                ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
                HttpServletRequest request = attributes.getRequest();
                paramValue = request.getAttribute("currentUser");
            }

        } catch (Exception e) {
            e.printStackTrace();
            if (StrKit.isBlank(param.defaultValue()) && e instanceof JSONException) {
                throw new BizException(StrKit.notBlank(e.getMessage()) ? e.getMessage() : "参数转换错误！");
            }
            if (StrKit.notBlank(param.defaultValue())) {
                field.set(this, JSONObject.parseObject(param.defaultValue(), field.getType()));
            }
            return;
        }

        if (paramValue == null && !param.required()) {
            if (StrKit.notBlank(param.defaultValue())) {
                field.set(this, JSONObject.parseObject(param.defaultValue(), field.getType()));
            }

            return;
        }

        if (paramValue == null && param.required()) {
            throw new BizException("参数【" + paramName + "】不能为空！");
        }

        // 如果是数组
        if (paramValue instanceof JSONArray && field.getGenericType() != null && field.getGenericType() instanceof ParameterizedType) {
            ParameterizedType pt = (ParameterizedType) field.getGenericType();
            paramValue = JSONArray.parseArray(JSONArray.toJSONString(paramValue), (Class<?>) pt.getActualTypeArguments()[0]);
        }

        field.set(this, paramValue);
    }

    private Cookie getCookieObject(String name) {
        Cookie[] cookies = context.getRequest().getCookies();
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if (cookie.getName().equals(name)) {
                    return cookie;
                }
            }
        }
        return null;
    }

    public String getCookie(String name, String defaultValue) {
        Cookie cookie = getCookieObject(name);
        return cookie != null ? cookie.getValue() : defaultValue;
    }

    /**
     * 获取cookie，如果不存在，则返回null
     *
     * @param name
     * @return
     */
    public String getCookie(String name) {
        return getCookie(name, null);
    }

    /**
     * 获取sesssionId，没有则创建
     *
     * @return
     */
    public String getSid() {
        return getSid(true);
    }

    /**
     * Set Cookie to response.
     * @param name cookie name
     * @param value cookie value
     * @param maxAgeInSeconds -1: clear cookie when close browser. 0: clear cookie immediately.  n>0 : max age in n seconds.
     * @param path see Cookie.setPath(String)
     * @param domain the domain name within which this cookie is visible; form is according to RFC 2109
     */
    public void setCookie(String name, String value, int maxAgeInSeconds, String path, String domain) {
        Cookie cookie = new Cookie(name, value);
        if (domain != null)
            cookie.setDomain(domain);
        cookie.setMaxAge(maxAgeInSeconds);
        cookie.setPath(path);
        context.getResponse().addCookie(cookie);
    }

    public void setCookie(String name, String value, int maxAgeInSeconds) {
        setCookie(name, value, maxAgeInSeconds, "/", null);
    }

    /**
     * 获取SessionId
     *
     * @param create
     *      如果不存在，是否创建
     * @return
     */
    public String getSid(boolean create) {
        String sid = getCookie("sid");
        if (StrKit.isBlank(sid) && create) {
            sid = Util.randomUUID();
            setCookie("sid", sid, 7 * 24 * 3600);
        }

        return sid;
    }

    /**
     * 初始化参数
     *
     * @throws Exception
     */
    protected abstract void init() throws Exception;

    /**
     * 处理业务
     *
     * @throws Exception
     */
    protected abstract void doCommand() throws Exception;

    /**
     * 做收尾工作
     *
     * @throws Exception
     */
    protected void doFinally() throws Exception {}

}
